package com.compression.tool.ui;

import com.compression.tool.model.task.FileEntry;
import com.compression.tool.util.FileUtils;
import com.compression.tool.util.PreviewUtils;
import com.github.junrar.Archive;
import com.github.junrar.exception.RarException;
import com.github.junrar.rarfile.FileHeader;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author 自然醒
 * @version 1.0
 */

//文件预览面板
public class FilePreviewPanel extends JPanel {

    private static final Logger logger = Logger.getLogger(FilePreviewPanel.class.getName());

    private JTextField filePathTextField; //添加文件路径文本框
    private JButton browseButton; //添加浏览按钮
    private JTextArea previewTextArea; // 添加文本区域
    private JLabel imageLabel; //添加图片标签
    private JTabbedPane tabbedPane; // 添加标签页
    private JTree tree; //压缩文件树形图
    private JScrollPane scrollPane; //树形视图的滚动面板

    //当前选中的压缩文件
    private File currentCompressedFile;

    /**
     * 构造方法
     */
    public FilePreviewPanel() {
        setLayout(new BorderLayout());
        initComponents();
        layoutComponents();
    }

    /**
     * 初始化组件
     */
    private void initComponents() {
        filePathTextField = new JTextField();
        browseButton = new JButton("浏览");
        browseButton.addActionListener(
                e -> {
                    browseForFile();
                }
        );

        previewTextArea = new JTextArea();
        previewTextArea.setEditable(false);

        imageLabel = new JLabel();
        imageLabel.setHorizontalAlignment(JLabel.CENTER);

        //初始化压缩文件树
        tree = new JTree();
        tree.setRootVisible(false);
        tree.setShowsRootHandles(true);
        //对其添加监听器
        tree.addTreeSelectionListener(
                new TreeSelectionListener() {
                    @Override
                    public void valueChanged(TreeSelectionEvent e) {
                        //todo
                        //这里可以实现单击预览功能
                    }
                }
        );

        //双击监听器
        tree.addMouseListener(
                new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        if (e.getClickCount() == 2) {
                            onTreeItemDoubleClick();
                        }
                    }
                }
        );


        scrollPane = new JScrollPane(tree);

        tabbedPane = new JTabbedPane();
        tabbedPane.addTab("文本预览", new JScrollPane(previewTextArea));
        tabbedPane.addTab("图像预览", new JScrollPane(imageLabel));
        tabbedPane.add("压缩文件结构", scrollPane);
    }

    /**
     * 布局组件
     */
    private void layoutComponents() {
        // 文件选择面板
        JPanel filePanel = new JPanel(new BorderLayout(5, 5));
        filePanel.setBorder(BorderFactory.createTitledBorder("选择预览文件"));
        filePanel.add(filePathTextField, BorderLayout.CENTER);
        filePanel.add(browseButton, BorderLayout.EAST);

        // 预览面板
        JPanel previewPanel = new JPanel(new BorderLayout());
        previewPanel.setBorder(BorderFactory.createTitledBorder("文件预览"));
        previewPanel.add(tabbedPane, BorderLayout.CENTER);

        add(filePanel, BorderLayout.NORTH);
        add(previewPanel, BorderLayout.CENTER);
    }

    // 浏览文件
    private void browseForFile() {
        JFileChooser fileChooser = new JFileChooser();//文件选择器
        int result = fileChooser.showOpenDialog(this);//显示文件选择器
        if (result == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            filePathTextField.setText(selectedFile.getAbsolutePath());
            previewFile(selectedFile);
        }
    }

    //展示选中的文件
    private void previewFile(File file) {
        String fileExtension = file.getName().toLowerCase();
        //展示压缩文件
        if (FileUtils.isValidArchive(file)) {
            tabbedPane.setSelectedIndex(0);
            showCompressedFile(file);
            currentCompressedFile = file;
            previewCompressedFile(file);
        } else if (fileExtension.endsWith(".txt") || fileExtension.endsWith(".md")
                || fileExtension.endsWith(".java") || fileExtension.endsWith(".xml")
                || fileExtension.endsWith(".html")) {
            //文本文件浏览
            tabbedPane.setSelectedIndex(0);
            previewTextFile(file);
        } else if (fileExtension.endsWith("png") || fileExtension.endsWith("jpg")
                || fileExtension.endsWith("jpeg") || fileExtension.endsWith("gif")
                || fileExtension.endsWith("bmp") || fileExtension.endsWith("svg")
        ) {
            //图片文件浏览
            tabbedPane.setSelectedIndex(1);
            previewImageFile(file);
        } else {
            tabbedPane.setSelectedIndex(0);
            previewTextArea.setText("暂不支持该文件类型");
            imageLabel.setIcon(null);
        }
    }


    //预览文本文件
    private void previewTextFile(File file) {
        try {
            StringBuilder context = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                String line;
                int count = 0;
                while ((line = reader.readLine()) != null && count < 1000) {
                    context.append(line).append("\n");
                    count++;
                }
                if (line != null) {
                    context.append("...");
                    context.append("\n").append("...省略部分内容");
                }
                previewTextArea.setText(context.toString());
            }
        } catch (IOException e) {
            previewTextArea.setText("无法读取文件: " + e.getMessage());
        }
    }

    //预览图片文件
    private void previewImageFile(File file) {
        ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
        // 缩放图像以适应预览区域
        if (imageIcon.getIconWidth() > 600 || imageIcon.getIconHeight() > 400) {
            Image scaledImage = imageIcon.getImage().getScaledInstance(
                    600, 400, Image.SCALE_SMOOTH);
            imageIcon = new ImageIcon(scaledImage);
        }

        imageLabel.setIcon(imageIcon);
        imageLabel.setText(null);
    }

    //展示压缩文件
    private void showCompressedFile(File file) {
        try {
            String fileTree = PreviewUtils.generateCompressedFileTree(file);
            previewTextArea.setText(fileTree);
        } catch (Exception e) {
            String errorMessage = "无法读取压缩文件: " + e.getMessage() + "\n\n";
            errorMessage += "支持的格式: ZIP, RAR, 7z, TAR, GZIP\n";
            errorMessage += "可能的原因:\n";
            errorMessage += "1. 文件已损坏\n";
            errorMessage += "2. 文件需要密码\n";
            errorMessage += "3. 不支持的压缩格式\n";
            errorMessage += "4. 文件版本过新\n";

            previewTextArea.setText(errorMessage);
            logger.warning("预览压缩文件失败: " + e.getMessage());
        }
    }

    //预览压缩文件显示内容
    private void previewCompressedFile(File file) {
        try {
            PreviewUtils.compressedFileCache.clear();
            DefaultMutableTreeNode root = PreviewUtils.buildCompressedFileTree(file);
            DefaultTreeModel treeModel = new DefaultTreeModel(root);
            tree.setModel(treeModel);
            for (int i = 0; i < tree.getRowCount(); i++) {
                tree.expandRow(i);
            }
        } catch (Exception e) {
            String errorMessage = "无法读取压缩文件: " + e.getMessage() + "\n\n";
            errorMessage += "支持的格式: ZIP, RAR, 7z, TAR, GZIP\n";
            errorMessage += "可能的原因:\n";
            errorMessage += "1. 文件已损坏\n";
            errorMessage += "2. 文件需要密码\n";
            errorMessage += "3. 不支持的压缩格式\n";
            errorMessage += "4. 文件版本过新\n";

            previewTextArea.setText(errorMessage);
            logger.warning("预览压缩文件失败: " + e.getMessage());
        }
    }

    /**
     * 树节点双击事件
     */
    private void onTreeItemDoubleClick() {
        TreePath selectedPath = tree.getSelectionPath();
        if (selectedPath == null) {
            return;
        }
        DefaultMutableTreeNode lastPathComponent = (DefaultMutableTreeNode) selectedPath.getLastPathComponent();
        FileEntry fileEntry = (FileEntry) lastPathComponent.getUserObject();
        //只处理文件节点 忽略目录节点
        if (!fileEntry.getType().equals("file")) {
            return;
        }
        try {
            //预览文件条目
            previewCompressedFileEntry(fileEntry, selectedPath);
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this,
                    "无法预览文件: " + e.getMessage(),
                    "错误",
                    JOptionPane.ERROR_MESSAGE);
            logger.warning("预览压缩文件条目失败: " + e.getMessage());
        }
    }

    /**
     * 预览文件条目内容
     */
    private void previewCompressedFileEntry(FileEntry fileEntry, TreePath selectedPath) throws Exception {
        // 构建文件路径
        StringBuilder filePath = new StringBuilder();
        Object[] pathComponents = selectedPath.getPath();
        for (int i = 1; i < pathComponents.length; i++) {
            // 从1开始跳过根节点
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) pathComponents[i];
            FileEntry entry = (FileEntry) treeNode.getUserObject();
            filePath.append(entry.getName());
            if (i < pathComponents.length - 1) {
                filePath.append("/");
            }
        }

        String entryPath = filePath.toString();
        Object entryObject = PreviewUtils.compressedFileCache.get(entryPath);

        if (entryObject == null) {
            throw new Exception("未找到文件条目：" + entryPath);
        }

        // 获取文件类型进行浏览
        String fileName = fileEntry.getName().toLowerCase();

        if (fileName.endsWith(".txt") || fileName.endsWith(".md")
                || fileName.endsWith(".java") || fileName.endsWith(".py") || fileName.endsWith(".cpp")
                || fileName.endsWith(".html") || fileName.endsWith(".css") || fileName.endsWith(".js")
                || fileName.endsWith(".xml") || fileName.endsWith(".json") || fileName.endsWith(".properties")) {
            // 文本浏览
            String content = extractTextContent(entryObject);
            previewTextArea.setText(content);
            tabbedPane.setSelectedIndex(0); // 切换到文本面板
        } else if (fileName.endsWith(".png") || fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")
                || fileName.endsWith(".gif") || fileName.endsWith(".bmp") || fileName.endsWith(".ico")) {
            // 图片浏览
            ImageIcon imageIcon = extractImageContent(entryObject);
            if (imageIcon != null) {
                // 缩放图像以适应预览区域
                if (imageIcon.getIconWidth() > 600 || imageIcon.getIconHeight() > 400) {
                    Image scaledImage = imageIcon.getImage().getScaledInstance(
                            600, 400, Image.SCALE_SMOOTH
                    );
                    imageIcon = new ImageIcon(scaledImage);
                }
                imageLabel.setIcon(imageIcon);
                imageLabel.setText(null);
                tabbedPane.setSelectedIndex(1); // 切换到图片面板
            }
        } else {
            // 显示文件基本信息
            String info = "文件: " + fileEntry.getName() + "\n" +
                    "大小: " + FileUtils.formatFileSize(fileEntry.getSize()) + "\n" +
                    "类型: " + getFileTypeDescription(fileName) + "\n\n" +
                    "不支持预览此文件类型";
            previewTextArea.setText(info);
            tabbedPane.setSelectedIndex(0);
        }
    }

    /**
     * 获取文件类型描述
     */
    private String getFileTypeDescription(String fileName) {
        if (fileName.endsWith(".exe") || fileName.endsWith(".dll")) {
            return "可执行文件";
        } else if (fileName.endsWith(".pdf")) {
            return "PDF文档";
        } else if (fileName.endsWith(".doc") || fileName.endsWith(".docx")) {
            return "Word文档";
        } else if (fileName.endsWith(".xls") || fileName.endsWith(".xlsx")) {
            return "Excel文档";
        } else if (fileName.endsWith(".zip") || fileName.endsWith(".rar") || fileName.endsWith(".7z")) {
            return "压缩文件";
        } else {
            return "未知文件类型";
        }
    }

    private String extractTextContent(Object entryObject) throws Exception {
        //创建临时文件
        File tempFile = File.createTempFile("preview_", ".tmp");
        tempFile.deleteOnExit();

        try {
            //根据条目类型进行不同处理
            if (entryObject instanceof ZipEntry) {
                extractZipEntry((ZipEntry) entryObject, tempFile);
            } else if (entryObject instanceof FileHeader) {
                extractRarEntry((FileHeader) entryObject, tempFile);
            } else if (entryObject instanceof SevenZArchiveEntry) {
                extractSevenZEntry((SevenZArchiveEntry) entryObject, tempFile);
            } else if (entryObject instanceof File) {
                //GZIP文件 直接读取
                extractGzContent((File) entryObject, tempFile);
            } else {
                throw new IllegalArgumentException("不支持的条目类型: " + entryObject.getClass().getName());
            }

            // 检查文件大小，避免读取过大文件
            if (tempFile.length() > 10 * 1024 * 1024) { // 10MB限制
                return "文件过大，无法预览（超过10MB限制）";
            }

            //读取临时文件内容
            StringBuilder content = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new FileReader(tempFile))) {
                String line;
                int lineCount = 0;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                    lineCount++;
                }
                if (line != null) {
                    content.append("...\n...省略部分内容");
                }
            }
            return content.toString();
        } finally {
            //确保临时文件删除
            if (tempFile.exists()) {
                tempFile.delete();
            }
        }
    }

    private ImageIcon extractImageContent(Object entryObject) throws Exception {
        //创建临时文件
        File tempFile = File.createTempFile("preview_img_", ".tmp");
        tempFile.deleteOnExit();

        try {
            // 根据条目类型进行不同处理
            if (entryObject instanceof ZipEntry) {
                extractZipEntry((ZipEntry) entryObject, tempFile);
            } else if (entryObject instanceof FileHeader) {
                extractRarEntry((FileHeader) entryObject, tempFile);
            } else if (entryObject instanceof SevenZArchiveEntry) {
                extractSevenZEntry((SevenZArchiveEntry) entryObject, tempFile);
            } else {
                throw new IllegalArgumentException("不支持的条目类型: " + entryObject.getClass().getName());
            }

            // 验证文件是否为有效图像
            if (!isValidImageFile(tempFile)) {
                throw new IOException("文件不是有效的图像格式");
            }

            // 创建ImageIcon
            return new ImageIcon(tempFile.getAbsolutePath());

        } finally {
            // 确保临时文件被删除
            if (tempFile.exists()) {
                tempFile.delete();
            }
        }
    }

    /**
     * 验证文件是否为有效的图像文件
     *
     * @param file
     * @return
     */
    private boolean isValidImageFile(File file) {
        try {
            ImageIcon imageIcon = new ImageIcon(file.getAbsolutePath());
            return imageIcon.getIconWidth() > 0 && imageIcon.getIconHeight() > 0;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 提取ZIP条目
     *
     * @param entry
     * @param outputFile
     * @throws IOException
     */
    private void extractZipEntry(ZipEntry entry, File outputFile) throws IOException {
        try (ZipFile zipFile = new ZipFile(currentCompressedFile);
             FileOutputStream fos = new FileOutputStream(outputFile);
             InputStream is = zipFile.getInputStream(entry)) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = is.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }
        }
    }

    /**
     * 提取RAR条目
     *
     * @param entry
     * @param outputFile
     * @throws IOException
     */
    private void extractRarEntry(FileHeader entry, File outputFile) throws IOException {
        try (Archive archive = new Archive(currentCompressedFile);
             FileOutputStream fos = new FileOutputStream(outputFile)) {
            //检查档案是否打开成功
            if (archive == null) {
                throw new RuntimeException("无法打开档案");
            }
            //检查是否加密
            if (archive.isEncrypted()) {
                throw new RuntimeException("已经加密无法打开");
            }
            //提取文件
            archive.extractFile(entry, fos);
        } catch (RarException e) {
            throw new IOException("提取rar条目失败:" + e.getMessage(), e);
        }
    }

    /**
     * 提取7Z条目
     *
     * @param entry
     * @param outputFile
     * @throws IOException
     */
    private void extractSevenZEntry(SevenZArchiveEntry entry, File outputFile) throws IOException {
        try (SevenZFile sevenZFile = new SevenZFile(currentCompressedFile);
             FileOutputStream fos = new FileOutputStream(outputFile)) {
            SevenZArchiveEntry currentEntry;
            //遍历找到目标条目
            while ((currentEntry = sevenZFile.getNextEntry()) != null) {
                if (currentEntry.getName() != null && currentEntry.getName().equals(entry.getName())) {
                    //读取条目数据
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    while ((bytesRead = sevenZFile.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                    break;
                }
            }
        } catch (Exception e) {
            throw new IOException("提取7z条目失败:" + e.getMessage(), e);
        }
    }

    /**
     * 提取GZ文件内容
     *
     * @param gzFile
     * @param outputFile
     * @throws IOException
     */
    private void extractGzContent(File gzFile, File outputFile) throws IOException {
        try (GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream(gzFile));
             FileOutputStream fos = new FileOutputStream(outputFile)) {
            byte[] buffer = new byte[1024];
            int length;
            while ((length = gzipInputStream.read(buffer)) > 0) {
                fos.write(buffer, 0, length);
            }
        }
    }
}
